package jia;

import java.awt.Point;
import java.util.*;

import prop.PropertiesReader;

import env.WorldModel;

import arch.CowboyArch;
import arch.LocalWorldModel;
import jason.asSemantics.*;
import jason.asSyntax.*;
import jason.environment.grid.Location;

/**
 * Is used when the leader delegates a new target to the agent. If the agent
 * already has a target the revise_target should be used.
 * 
 */
public class delegate_attack_target extends DefaultInternalAction {

	astar pathFinder;
	LocalWorldModel model;
	TransitionSystem ts;
	Point enemyPoint;
	SwitchFinder sf;
	@Override
	public Object execute(TransitionSystem ts, Unifier un, Term[] args) throws Exception {
		model = ((CowboyArch) ts.getUserAgArch()).getModel();
		this.ts = ts;

		
		int agX = (int) ((NumberTerm) args[0]).solve();
		int agY = (int) ((NumberTerm) args[1]).solve();
		Atom currentAgentAtom = (Atom) args[2];
		String currentAgent = currentAgentAtom.getFunctor().equals("self") ? ts.getUserAgArch().getAgName() : currentAgentAtom.getFunctor();
		int id = CowboyArch.getAgId(currentAgent);
		
		
		Point p = null;

		if(model.hasObject(model.ENEMYCORRAL, agX,agY))
		{
			p = ClearEnemyCorral(model.getAgPos(id).x,model.getAgPos(id).y);
		}
		
		if(p == null && CowboyArch.getAgId(currentAgent) != model.firstWarrior)
		{
			if(KeepLocation(id) && CowsInEnemyCorral())
			{
				p = new Point();
				p.x = agX;
				p.y = agY;
			}
			else
			{
				
				p = GoToSwitch(id);
				if(p != null && TargetTaken(p,id))
				{
					p = null;
				}
			}
		}
		if(p == null)
		{
			p = DetermineTarget(currentAgent, agX, agY);
		}
		
		
		model.setTargetAgentPos(id, p.x, p.y);
		return un.unifies(args[4], new NumberTermImpl(p.x)) && un.unifies(args[5], new NumberTermImpl(p.y));
			
		
	}
	private boolean CowsInEnemyCorral()
	{
		for(int y = 1; y < model.getHeight()-1; y++)
			for(int x = 1; x < model.getWidth()-1; x++)
				if(model.hasObject(model.ENEMYCORRAL, x,y) && model.hasObject(model.COW, x, y))
					return true;
		return false;
	}
	private Point ClearEnemyCorral(int x, int y)
	{
		//FindLeftBorder
		int leftBorder = x;
		while(model.hasObject(WorldModel.ENEMYCORRAL, leftBorder, y))
			leftBorder--;
		
		int rightBorder = x;
		while(model.hasObject(WorldModel.ENEMYCORRAL, rightBorder, y))
			rightBorder++;
		
		int lowerBorder = y;
		while(model.hasObject(WorldModel.ENEMYCORRAL, x, lowerBorder))
			lowerBorder++;
		
		int upperBorder = y;
		while(model.hasObject(WorldModel.ENEMYCORRAL, x, upperBorder))
			upperBorder--;
		
		//Dont hunt cows too close to obstacle
		int DIST_TO_FENCE = 2; 
		for(int i = leftBorder + 1 + DIST_TO_FENCE; i < rightBorder - DIST_TO_FENCE; i++){
			for(int j = upperBorder + 1 + DIST_TO_FENCE; j < lowerBorder - DIST_TO_FENCE; j++){
				if(model.hasObject(WorldModel.COW, i, j))
					return new Point(i,j);
			}
		}
		
		//Move Randomly
		
		//List of points in corral adjacent to the agent
		ArrayList<Point> adjCorPoints = new ArrayList<Point>();
		
		for(int i = -1; i <= 1; i++){
			for(int j = -1; j <= 1; j++){
				if(i + x >= 0 && i + x < model.getWidth() &&
					j + y >= 0 && j + y < model.getHeight() &&
					model.hasObject(WorldModel.ENEMYCORRAL, x + i, y + j))
					adjCorPoints.add(new Point(x + i, y + j));
			}
		}
		
		int choice = (new Random()).nextInt(adjCorPoints.size());
		
		return adjCorPoints.get(choice);
		
		/*
		Point p = null;
		int i = 1;
		//explore correl
		while(i < 20)
		{
			if(model.hasObject(model.ENEMYCORRAL, x, y+i-1) && model.agentVisits[x][y+i] == 0)
			{
				p = new Point();
				p.x = x;
				p.y = y+i;
				return p;
			}
			else if(model.hasObject(model.ENEMYCORRAL, x, y-(i-1)) && model.agentVisits[x][y-i] == 0)
			{
				p = new Point();
				p.x = x;
				p.y = y-i;
				return p;
			}
			else if(model.hasObject(model.ENEMYCORRAL, x+i-1,y) && model.agentVisits[x+i][y] == 0)
			{
				p = new Point();
				p.x = x+i;
				p.y = y;
				return p;
			}
			else if(model.hasObject(model.ENEMYCORRAL, x-(i-1),y) && model.agentVisits[x-i][y] == 0)
			{
				p = new Point();
				p.x = x+i;
				p.y = y;
				return p;
			}
			
			i++;
		}
		return GetCorralPoint(x,y);*/
	}
	private Point GetCorralPoint(int x, int y)
	{
		Point p = new Point();
		int right = 0;
		int down = 0;
		int left = 0;
		int up = 0;
		
		while(model.hasObject(model.ENEMYCORRAL, x+right+1, y))
			right++;
		while(model.hasObject(model.ENEMYCORRAL, x, y+down+1))
			down++;
		while(model.hasObject(model.ENEMYCORRAL, x-left-1, y))
			left++;
		while(model.hasObject(model.ENEMYCORRAL, x, y-up-1))
			up++;

		
		if(right < left)
			p.x = x-left;
		else
			p.x = x+right;
		if(down < up)
			p.y = y-up;
		else
			p.y = y+down;
		
		return p;
		
		
	}

	private Point DetermineTarget(String currentAgent,int agX,int agY)
	{
		if(enemyPoint != null || model.EnemyCorralPoint != null)
		{
			if(enemyPoint == null)
				enemyPoint = model.EnemyCorralPoint;
			
			return enemyPoint;
		}
//		
//		Point p; 
//		Location l = model.targets[CowboyArch.getAgId(currentAgent)];
//		if(l != null && agX != l.x && agY != l.y)
//		{
//			p = new Point();
//			p.x = l.x;
//			p.y = l.y;
//			return p;
//		}
			
		return GuessOpponentCorrel();
	}
	private Point GuessOpponentCorrel()
	{
		Point p = new Point();
		p.x = model.getWidth()  - model.getCorralCenter().x;
		p.y = model.getHeight()  - model.getCorralCenter().y;
		
		if(model.agentVisits[p.x][p.y] > 0)
		{
			p.x = model.getCorralCenter().x;
			p.y = model.getHeight()  - model.getCorralCenter().y;
		}
		if(model.agentVisits[p.x][p.y] > 0)
		{
			p.x = model.getWidth()  - model.getCorralCenter().x;
			p.y = model.getCorralCenter().y;
		}
		if(model.agentVisits[p.x][p.y] > 0)
		{
			// go to random unexplored location
			p.x = 0;
			p.y = 0;
		}
		return p;
	}
	private boolean TargetTaken(Point p, int id)
	{
		int i = 0;
		for(Location l : model.targets)
		{ 
			if(l != null && i != id && l.x == p.x && l.y == p.y)
			{
				return true;
			}
			i++;
		}
		return false;
	}
	private boolean KeepLocation(int id)
	{
		Location location = model.getAgPos(id);
		if(IsNextToSwitch(location) != null && IsCorralSwitch(location))
		{
			return true;
		}
		else 
			return false;
		
	}
	private Point GoToSwitch(int id)
	{
		int friend = 0;
		if(id == model.lastWarrior)
			friend = model.lastWarrior - 1;
		else
			friend = model.lastWarrior;
		Location friendLocation = model.getAgPos(friend);
		Location friendTarget = model.targets[friend];
		if(friendLocation != null && friendTarget != null)
		{
			Point p = IsNextToSwitch(friendLocation);
			if(p != null && !IsCorralSwitch(friendLocation) &&  (friendTarget.x-friendLocation.x > 0 || friendTarget.y-friendLocation.y > 0))
			{
				return p;
			}
		}
		
		Location leaderPosition = model.getAgPos(model.firstWarrior);
		if(leaderPosition == null)
			return null;
		Point fencePoint = StandingNextToFence(leaderPosition.x,leaderPosition.y);
			
		if(fencePoint != null && fencePoint.x != 0 && fencePoint.y != 0)
		{
			Location leaderTarget = model.targets[model.firstWarrior];
			// only if fence is in target direction
			if(leaderTarget != null && (leaderTarget.x - fencePoint.x > 0 || leaderTarget.y - fencePoint.y > 0))
				return FindSwitch(fencePoint, leaderPosition);
		}

		return null;
	}
	private boolean IsCorralSwitch(Location l)
	{
		boolean correl = false;
	
		for(int i = 0; i < 4; i++)
		{
			if(model.hasObject(model.ENEMYCORRAL, l.x+i,l.y+i) || model.hasObject(model.ENEMYCORRAL, l.x-i,l.y-i))
			{
				correl = true;
				break;
			}
		}
		return correl;
	}
	private Point IsNextToSwitch(Location l)
	{
		Point p = null;
		if(model.hasObject(model.SWITCH, l.x, l.y-1))
		{
			p = new Point();
			p.x = l.x;
			p.y = l.y-2;	
		}
		if(model.hasObject(model.SWITCH, l.x, l.y+1))
		{
			p = new Point();
			p.x = l.x;
			p.y = l.y+2;	
		}
		if(model.hasObject(model.SWITCH, l.x-1, l.y))
		{
			p = new Point();
			p.x = l.x-2;
			p.y = l.y;	
		}
		if(model.hasObject(model.SWITCH, l.x+1, l.y))
		{
			p = new Point();
			p.x = l.x+2;
			p.y = l.y;	
		}
		return p;
		
		
	}
	private Point FindSwitch(Point fencePoint, Location leaderPosition)
	{
		Point nextFencePoint = StandingNextToFence(fencePoint.x, fencePoint.y);
		//horizontal fence
		if(nextFencePoint.x - fencePoint.x != 0)
		{ 
			Point p = new Point();
			int i = 1;
			int k = 1;
			while(model.hasObject(model.FENCE, fencePoint.x+i, fencePoint.y))
			{
				i++;
			}
			while(model.hasObject(model.FENCE, fencePoint.x-i, fencePoint.y))
			{
				k++;
			}
			if(model.hasObject(model.SWITCH, fencePoint.x+i, fencePoint.y))
			{
				p.x = fencePoint.x+i;
				p.y = fencePoint.y + leaderPosition.y - fencePoint.y;
			}
			else if(model.hasObject(model.SWITCH, fencePoint.x-k, fencePoint.y))
			{
				
				p.x = fencePoint.x-k;
				p.y = fencePoint.y + leaderPosition.y - fencePoint.y;
			}
			else if(model.agentVisits[fencePoint.x+i][fencePoint.y] == 0)
			{
				p.x = fencePoint.x+i;
				p.y = fencePoint.y + leaderPosition.y - fencePoint.y;
			}
			else if(model.agentVisits[fencePoint.x-k][fencePoint.y] == 0)
			{
				p.x = fencePoint.x-k;
				p.y = fencePoint.y + leaderPosition.y - fencePoint.y;
			}
			return p;

		}
		//vertical fence
		else
		{
			Point p = new Point();
			int i = 1;
			int k = 1;
			while(model.hasObject(model.FENCE, fencePoint.x, fencePoint.y+i))
			{
				i++;
			}
			while(model.hasObject(model.FENCE, fencePoint.x, fencePoint.y-k))
			{
				k++;
			}

			if(model.hasObject(model.SWITCH, fencePoint.x, fencePoint.y+i))
			{
				p.x = fencePoint.x+ leaderPosition.x - fencePoint.x;
				p.y = fencePoint.y+i;
			}
			else if(model.hasObject(model.SWITCH, fencePoint.x, fencePoint.y-k))
			{
				p.x = fencePoint.x+ leaderPosition.x - fencePoint.x;
				p.y = fencePoint.y-k;
			}
			else if(model.agentVisits[fencePoint.x][fencePoint.y+i] == 0)
			{
				p.x = fencePoint.x+ leaderPosition.x - fencePoint.x;
				p.y = fencePoint.y+i;
			}
			else if(model.agentVisits[fencePoint.x][fencePoint.y-k] == 0)
			{
				p.x = fencePoint.x+ leaderPosition.x - fencePoint.x;
				p.y = fencePoint.y-k;
			}
			return p;
		}
	}
	
	private Point StandingNextToFence(int x, int y)
	{
		Point p = new Point();
		if(model.hasObject(model.FENCE, x, y+1))
		{
			p.x = x;
			p.y = y+1;
		}
		if(model.hasObject(model.FENCE, x, y-1))
		{
			p.x = x;
			p.y = y-1;
		}
		if(model.hasObject(model.FENCE, x+1, y))
		{
			p.x = x+1;
			p.y = y;
		}
		if(model.hasObject(model.FENCE, x-1, y))
		{
			p.x = x-1;
			p.y = y;
		}
		if(model.hasObject(model.FENCE, x+1, y+1))
		{
			p.x = x+1;
			p.y = y+1;
		}
		if(model.hasObject(model.FENCE, x+1, y-1))
		{
			p.x = x+1;
			p.y = y-1;
		}
		if(model.hasObject(model.FENCE, x-1, y+1))
		{
			p.x = x-1;
			p.y = y+1;
		}
		if(model.hasObject(model.FENCE, x-1, y-1))
		{
			p.x = x-1;
			p.y = y-1;
		}
		return p;
	}
}